#include <linux/module.h>
#include <linux/init.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#define GPIO_LED_NAME   "gpio_led"
struct led_dev
{
    dev_t dev_num;          //设备号
    struct cdev cdev_led;   //字符设备结构体
    struct class *class;    //所属的类
    struct device *device;  //设备结构体
    struct device_node *nd; //设备树节点
    int major;              //主设备号
    int minor;              //次设备号
    int gpio_led;           //led的gpio号

};
static struct led_dev gpioled;
int led_open(struct inode *inode, struct file *filp)
{
    /* 文件描述符对应的内核中的file */
    filp->private_data=&gpioled;
    return 0;
}
ssize_t led_read(struct file *filp, char __user *buf, size_t size, loff_t * off_t)
{
    return 0;
}
ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t * off_t)
{
    int retval;
    char tmp_buf[8];
    long led_state;
    struct led_dev *ldev=filp->private_data;
    
    retval=copy_from_user(tmp_buf,buf,size);
    printk( KERN_INFO "copy from user:%s\n",tmp_buf);
    if (retval<0)
		return -EFAULT;
    if (kstrtol(tmp_buf, 0, &led_state))
		return -EINVAL;
    if(led_state)//led on
        gpio_set_value(ldev->gpio_led,0);
    else//led off
        gpio_set_value(ldev->gpio_led,1);
    return 0;
}
int led_release(struct inode *node, struct file *filp)
{
    return 0;
}
static struct file_operations led_fops=
{
    .owner=THIS_MODULE,
    .open=led_open,
    .write=led_write,
    .read=led_read,
    .release=led_release,
};
static int __init led_init(void)
{
    int ret;
    //获取设备在设备树中的节点
    gpioled.nd=of_find_node_by_path("/gpioled");
    if(gpioled.nd==NULL)
        return -EINVAL;
    //get GPIO number to use with Linux generic GPIO API
    gpioled.gpio_led=of_get_named_gpio(gpioled.nd,"led-gpio",0);
    if(gpioled.gpio_led<0)
        return -EINVAL;
    //输出高电平，led默认关闭    
    ret=gpio_direction_output(gpioled.gpio_led,1);
    if(ret<0)
        return -EINVAL;
    //请求设备号
    alloc_chrdev_region(&gpioled.dev_num,0,1,GPIO_LED_NAME); 
    //初始化cdev
    gpioled.cdev_led.owner=THIS_MODULE;
    cdev_init(&gpioled.cdev_led,&led_fops);
    //cdev添加到系统
    cdev_add(&gpioled.cdev_led,gpioled.dev_num,1);
    //创建类    
    gpioled.class=class_create(THIS_MODULE,GPIO_LED_NAME);
    if(IS_ERR(gpioled.class))
        return PTR_ERR(gpioled.class);
    //创建设备
    gpioled.device=device_create(gpioled.class,NULL,gpioled.dev_num,NULL,GPIO_LED_NAME);
    if(IS_ERR(gpioled.device))
        return PTR_ERR(gpioled.device);
    printk("init OK\n");
    return 0;
}
static void __exit led_exit(void)
{
    if (gpio_is_valid(gpioled.gpio_led))
		gpio_free(gpioled.gpio_led);
    cdev_del(&gpioled.cdev_led);
    unregister_chrdev_region(gpioled.dev_num,1);
    device_destroy(gpioled.class,gpioled.dev_num);
    class_destroy(gpioled.class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("steve");